package com.example.common;

import com.example.exception.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
// import org.springframework.security.access.AccessDeniedException;
// import org.springframework.security.authentication.BadCredentialsException;
// import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * 全局异常处理器
 * 统一处理系统中的各种异常，提供详细的错误信息和用户友好的提示
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @Value("${spring.profiles.active:prod}")
    private String activeProfile;

    /**
     * 处理基础异常
     */
    @ExceptionHandler(BaseException.class)
    public ResponseEntity<Result<ErrorResponse>> handleBaseException(BaseException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = new ErrorResponse(e)
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId);

        // 开发环境添加堆栈信息
        if ("dev".equals(activeProfile)) {
            errorResponse.withStackTrace(getStackTrace(e));
        }

        // 记录日志
        logException(e, requestId, request);

        return ResponseEntity.status(errorResponse.getHttpStatus())
                .body(Result.error(errorResponse.getErrorMessage(), errorResponse));
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Result<ErrorResponse>> handleBusinessException(BusinessException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = new ErrorResponse(e)
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("操作失败，请检查输入信息或联系管理员");

        log.warn("业务异常 [{}]: {} - {}", requestId, e.getErrorCode(), e.getErrorMessage());

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error(e.getErrorMessage(), errorResponse));
    }

    /**
     * 处理系统异常
     */
    @ExceptionHandler(SystemException.class)
    public ResponseEntity<Result<ErrorResponse>> handleSystemException(SystemException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = new ErrorResponse(e)
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("系统暂时不可用，请稍后重试");

        if ("dev".equals(activeProfile)) {
            errorResponse.withStackTrace(getStackTrace(e));
        }

        log.error("系统异常 [{}]: {} - {}", requestId, e.getErrorCode(), e.getErrorMessage(), e);

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error("系统内部错误", errorResponse));
    }

    /**
     * 处理认证异常
     */
    @ExceptionHandler(com.example.exception.AuthenticationException.class)
    public ResponseEntity<Result<ErrorResponse>> handleAuthenticationException(
            com.example.exception.AuthenticationException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = new ErrorResponse(e)
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("身份验证失败，请重新登录");

        log.warn("认证异常 [{}]: {} - {}", requestId, e.getErrorCode(), e.getErrorMessage());

        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(Result.error(e.getErrorMessage(), errorResponse));
    }

    /*
     * 处理Spring Security认证异常
     * 注释掉，因为项目可能没有引入Spring Security
     */
    /*
    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<Result<ErrorResponse>> handleSpringAuthenticationException(
            AuthenticationException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.authentication(ErrorCode.AUTH_LOGIN_FAILED, "认证失败")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("用户名或密码错误");

        log.warn("Spring认证异常 [{}]: {}", requestId, e.getMessage());

        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(Result.error("认证失败", errorResponse));
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<Result<ErrorResponse>> handleAccessDeniedException(
            AccessDeniedException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.authorization(ErrorCode.PERMISSION_DENIED, "权限不足")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("您没有权限访问此资源");

        log.warn("权限异常 [{}]: {}", requestId, e.getMessage());

        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(Result.error("权限不足", errorResponse));
    }
    */

    /**
     * 处理参数验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Result<ErrorResponse>> handleValidationException(
            MethodArgumentNotValidException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });

        ErrorResponse errorResponse = ErrorResponse.validation(ErrorCode.DATA_VALIDATION_FAILED, "参数验证失败")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withDetails(errors)
                .withUserMessage("请检查输入参数");

        log.warn("参数验证异常 [{}]: {}", requestId, errors);

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error("参数验证失败", errorResponse));
    }

    /**
     * 处理约束验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Result<ErrorResponse>> handleConstraintViolationException(
            ConstraintViolationException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        Map<String, String> errors = new HashMap<>();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            String fieldName = violation.getPropertyPath().toString();
            String errorMessage = violation.getMessage();
            errors.put(fieldName, errorMessage);
        }

        ErrorResponse errorResponse = ErrorResponse.validation(ErrorCode.DATA_VALIDATION_FAILED, "数据验证失败")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withDetails(errors)
                .withUserMessage("请检查输入数据格式");

        log.warn("约束验证异常 [{}]: {}", requestId, errors);

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error("数据验证失败", errorResponse));
    }

    /**
     * 处理数据库异常
     */
    @ExceptionHandler(DataAccessException.class)
    public ResponseEntity<Result<ErrorResponse>> handleDataAccessException(
            DataAccessException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.system(ErrorCode.DATABASE_QUERY_ERROR, "数据库操作失败")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("数据操作失败，请稍后重试");

        if ("dev".equals(activeProfile)) {
            errorResponse.withStackTrace(getStackTrace(e));
        }

        log.error("数据库异常 [{}]: {}", requestId, e.getMessage(), e);

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error("数据库操作失败", errorResponse));
    }

    /**
     * 处理新版外键约束异常
     */
    @ExceptionHandler(com.example.exception.ForeignKeyException.class)
    public ResponseEntity<Result<ErrorResponse>> handleForeignKeyException(
            com.example.exception.ForeignKeyException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.business("FK001", e.getMessage())
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage(e.getMessage());

        // 添加外键约束详细信息
        errorResponse.addContext("entityType", e.getEntityType());
        errorResponse.addContext("entityName", e.getEntityName());
        errorResponse.addContext("operation", e.getOperation());
        errorResponse.addContext("constraintName", e.getConstraintName());
        
        if (e.getRelatedData() != null && !e.getRelatedData().isEmpty()) {
            errorResponse.addContext("relatedData", e.getRelatedData());
        }

        log.warn("外键约束异常 [{}]: {} - 实体类型:{}, 实体名称:{}, 操作:{}", 
                requestId, e.getMessage(), e.getEntityType(), e.getEntityName(), e.getOperation());

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error(e.getMessage(), errorResponse));
    }

    /**
     * 处理数据完整性违反异常
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<Result<ErrorResponse>> handleDataIntegrityViolationException(
            DataIntegrityViolationException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        String userMessage = "数据操作失败";
        String errorMessage = "数据完整性约束违反";
        String errorCode = ErrorCode.DATA_CONSTRAINT_VIOLATION;
        String suggestion = "请检查数据完整性要求后重试";

        // 解析外键约束错误
        String exceptionMsg = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
        
        if (exceptionMsg.contains("duplicate entry")) {
            userMessage = "数据已存在，请检查后重试";
            errorMessage = "数据重复错误";
            suggestion = "请检查是否存在重复的唯一字段值";
        } else if (exceptionMsg.contains("foreign key constraint")) {
            errorCode = ErrorCode.FOREIGN_KEY_CONSTRAINT_VIOLATION;
            userMessage = "存在关联数据，无法删除";
            errorMessage = "外键约束违反";
            suggestion = "请先删除或解除相关联的数据，再进行此操作";
            
            // 解析具体的外键约束信息
            if (exceptionMsg.contains("fk_user_role_user_id")) {
                userMessage = "用户正在被角色引用，无法删除";
                suggestion = "请先解除用户的角色分配，再删除用户";
            } else if (exceptionMsg.contains("fk_user_role_role_id")) {
                userMessage = "角色正在被用户使用，无法删除";
                suggestion = "请先解除角色与用户的绑定关系，再删除角色";
            } else if (exceptionMsg.contains("fk_role_menu_")) {
                userMessage = "角色或菜单存在绑定关系，无法删除";
                suggestion = "请先清除角色菜单权限配置，再进行删除";
            } else if (exceptionMsg.contains("fk_role_permission_")) {
                userMessage = "角色或权限存在绑定关系，无法删除";
                suggestion = "请先清除角色权限配置，再进行删除";
            } else if (exceptionMsg.contains("fk_user_balance_user_id")) {
                userMessage = "用户存在余额记录，无法删除";
                suggestion = "请先清空用户余额，再删除用户账户";
            } else if (exceptionMsg.contains("fk_recharge_record_user_id")) {
                userMessage = "用户存在充值记录，无法删除";
                suggestion = "充值记录是重要财务数据，建议禁用用户而不是删除";
            } else if (exceptionMsg.contains("fk_operation_log_user_id")) {
                userMessage = "用户存在操作日志，无法删除";
                suggestion = "操作日志是重要审计数据，建议禁用用户而不是删除";
            } else if (exceptionMsg.contains("fk_notice_publisher_id")) {
                userMessage = "用户发布过通知，无法删除";
                suggestion = "请先处理用户发布的通知，再删除用户";
            }
        } else if (exceptionMsg.contains("cannot be null")) {
            userMessage = "必填字段不能为空";
            errorMessage = "字段值为空错误";
            suggestion = "请填写所有必需的字段";
        } else if (exceptionMsg.contains("data too long")) {
            userMessage = "数据长度超出限制";
            errorMessage = "数据长度错误";
            suggestion = "请减少数据长度或检查字段大小限制";
        }

        ErrorResponse errorResponse = ErrorResponse.business(errorCode, errorMessage)
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage(userMessage)
                .withSuggestion(suggestion);

        // 开发环境添加详细的异常信息
        if ("dev".equals(activeProfile)) {
            errorResponse.addContext("originalException", exceptionMsg);
        }

        log.warn("数据完整性异常 [{}]: {}", requestId, e.getMessage());

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error(errorMessage, errorResponse));
    }

    /**
     * 处理404异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Result<ErrorResponse>> handleNoHandlerFoundException(
            NoHandlerFoundException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.business("40404", "请求的资源不存在")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("请求的页面或接口不存在");

        log.warn("404异常 [{}]: {} {}", requestId, e.getHttpMethod(), e.getRequestURL());

        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(Result.error("资源不存在", errorResponse));
    }

    /**
     * 处理方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Result<ErrorResponse>> handleMethodNotSupportedException(
            HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.business("40405", "请求方法不支持")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("请求方法不正确")
                .withSuggestion("支持的方法: " + String.join(", ", e.getSupportedMethods()));

        log.warn("方法不支持异常 [{}]: {} 不支持 {}", requestId, request.getRequestURI(), e.getMethod());

        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
                .body(Result.error("请求方法不支持", errorResponse));
    }

    /**
     * 处理缺少参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<Result<ErrorResponse>> handleMissingParameterException(
            MissingServletRequestParameterException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.validation(ErrorCode.PARAMETER_ERROR, "缺少必需参数")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("请求参数不完整")
                .addContext("missingParameter", e.getParameterName())
                .addContext("parameterType", e.getParameterType());

        log.warn("缺少参数异常 [{}]: 缺少参数 {} ({})", requestId, e.getParameterName(), e.getParameterType());

        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(Result.error("缺少必需参数: " + e.getParameterName(), errorResponse));
    }

    /**
     * 处理通用运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Result<ErrorResponse>> handleRuntimeException(
            RuntimeException e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.system(ErrorCode.SYSTEM_ERROR, "系统运行时错误")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("系统暂时不可用，请稍后重试");

        if ("dev".equals(activeProfile)) {
            errorResponse.withStackTrace(getStackTrace(e));
        }

        log.error("运行时异常 [{}]: {}", requestId, e.getMessage(), e);

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error("系统运行时错误", errorResponse));
    }

    /**
     * 处理通用异常（兜底处理）
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Result<ErrorResponse>> handleGenericException(
            Exception e, HttpServletRequest request) {
        String requestId = generateRequestId();

        ErrorResponse errorResponse = ErrorResponse.system(ErrorCode.UNKNOWN_ERROR, "未知系统错误")
                .withRequest(request.getRequestURI(), request.getMethod())
                .withRequestId(requestId)
                .withUserMessage("系统发生未知错误，请联系管理员");

        if ("dev".equals(activeProfile)) {
            errorResponse.withStackTrace(getStackTrace(e));
        }

        log.error("未知异常 [{}]: {}", requestId, e.getMessage(), e);

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error("系统错误", errorResponse));
    }

    // ========== 工具方法 ==========

    /**
     * 生成请求ID
     */
    private String generateRequestId() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }

    /**
     * 获取异常堆栈信息
     */
    private String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        return sw.toString();
    }

    /**
     * 记录异常日志
     */
    private void logException(BaseException e, String requestId, HttpServletRequest request) {
        String logLevel = e.getErrorLevel() != null ? e.getErrorLevel().name() : "ERROR";
        String category = e.getErrorCategory() != null ? e.getErrorCategory().name() : "UNKNOWN";

        String logMessage = String.format(
            "异常详情 [%s]: 分类=%s, 级别=%s, 错误码=%s, 消息=%s, 路径=%s %s",
            requestId, category, logLevel, e.getErrorCode(), e.getErrorMessage(),
            request.getMethod(), request.getRequestURI()
        );

        switch (logLevel) {
            case "WARN":
                log.warn(logMessage);
                break;
            case "ERROR":
            case "FATAL":
                log.error(logMessage, e);
                break;
            default:
                log.info(logMessage);
                break;
        }
    }
}